package cn.assumejson.springframework.beans.factory.support;

import cn.assumejson.springframework.beans.factory.BeansException;
import cn.assumejson.springframework.beans.factory.FactoryBean;
import cn.assumejson.springframework.beans.factory.config.BeanDefinition;
import cn.assumejson.springframework.beans.factory.config.BeanPostProcessor;
import cn.assumejson.springframework.beans.factory.config.ConfigurableBeanFactory;
import cn.assumejson.springframework.core.util.StringValueResolver;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ObjectUtil;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * @Author Mr. Li
 * @Date 2021/11/29 23:17
 * @Version 1.0
 */
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    /**
     * 属性解析器
     */
    private final List<StringValueResolver> embeddedValueResolvers = new LinkedList<>();


    @Override
    public Object getBean(String name) {
        return doGetBean(name, null, getBeanDefinition(name), null);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) {
        return (T) doGetBean(name, requiredType, getBeanDefinition(name), null);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, null, getBeanDefinition(name), args);
    }

    @SuppressWarnings("unchecked")
    private <T> T doGetBean(String beanName, final Class<T> requiredType, BeanDefinition bd, final Object[] args) {

        Object sharedInstance = null;
        try {
            // TODO parent属性合并 dependsOn
            if (bd.isSingleton()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    // 发生循环依赖！
                    sharedInstance = getSingleton(beanName);
                    return (T) getObjectForBeanInstance(sharedInstance, beanName);
                }
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        //进入创建bean的逻辑
                        return createBean(beanName, bd, args);
                    } catch (BeansException ex) {
                        //创建bean的过程中发生异常,需要销毁关于当前bean的所有信息
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
            }

            // TODO 获取FactoryBean & 开头的 问题
            // 判断FactoryBean
            if (sharedInstance != null && args == null) {
                // 如果是 FactoryBean，则需要调用 FactoryBean#getObject
                return (T) getObjectForBeanInstance(sharedInstance, beanName);
            }
            if ((ObjectUtil.isNotNull(requiredType) && requiredType.isAssignableFrom(bd.getBeanClass()))) {
                if (ObjectUtil.isNotNull(sharedInstance)) {
                    return (T) sharedInstance;
                }
            }

            Object bean = createBean(beanName, bd, args);
            return (T) getObjectForBeanInstance(bean, beanName);
        } catch (Exception e) {
            throw new BeansException("doCreate is fail", e);
        }

    }

    private Object getObjectForBeanInstance(Object beanInstance, String beanName) throws Exception {
        if (!(beanInstance instanceof FactoryBean)) {
            return beanInstance;
        }
        Object object = getCachedObjectForFactoryBean(beanName);

        if (object == null) {
            FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
            object = getObjectFromFactoryBean(factoryBean, beanName);
        }
        return object;
    }


    @Override
    public abstract boolean containsBean(String name);

    /**
     * 将Bean的实现都委托给该方法
     *
     * @param name beanName
     * @param bd   合并后的beanDefinition
     * @return Object
     */
    protected abstract Object createBean(String name, BeanDefinition bd) throws BeansException;

    protected abstract Object createBean(String name, BeanDefinition bd, Object... args) throws BeansException;

    /**
     * 获取BeanDefinition
     *
     * @param name beanName
     * @return BeanDefinition
     */
    protected abstract BeanDefinition getBeanDefinition(String name);

    @Override
    public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
        embeddedValueResolvers.add(valueResolver);
    }

    @Override
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");

        // Add to end of list
        this.beanPostProcessors.add(beanPostProcessor);
    }

    public List<BeanPostProcessor> getBeanPostProcessors() {
        return this.beanPostProcessors;
    }

    @Override
    public ClassLoader getBeanClassLoader() {
        return ClassLoaderUtil.getClassLoader();
    }

    @Override
    public String resolveEmbeddedValue(String value) {
        if (value == null) {
            return null;
        }
        String result = value;
        for (StringValueResolver resolver : this.embeddedValueResolvers) {
            result = resolver.resolveStringValue(result);
            if (result == null) {
                return null;
            }
        }
        return result;
    }

    @Override
    public Class<?> getType(String name) {
        return getType(name, true);
    }

    private Class<?> getType(String name, boolean b) {
        Object newInstance = getSingleton(name);
        if (newInstance == null) {
            return getBeanDefinition(name).getBeanClass();
        }
        return newInstance.getClass();
    }
}
